package com.crazymakercircle.Balance;

import com.crazymakercircle.constants.ServerConstants;
import com.crazymakercircle.entity.ImNode;
import com.crazymakercircle.util.JsonUtil;
import com.crazymakercircle.zk.CuratorZKclient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/*
 * create by 事了拂衣去
 **/
@Data
@Slf4j
public class ImLoadBalance
{

    //Zk客户端
    private CuratorFramework client = null;
    private String managerPath;


    public ImLoadBalance(CuratorZKclient curatorZKClient)
    {
        this.client = curatorZKClient.getClient();
        managerPath = ServerConstants.MANAGE_PATH;
    }

    /*
     * 获取负载最小的IM节点
     *
     * @return
     */
    public ImNode getBestWorker()
    {
        List<ImNode> workers = getWorkers();

        log.info("全部节点如下：");
        workers.stream().forEach(node ->
        {
            log.info("节点信息：{}", JsonUtil.pojoToJson(node));
        });
        ImNode best = balance(workers);

        return best;
    }

    /*
     * 按照负载排序
     *
     * @param items 所有的节点
     * @return 负载最小的IM节点
     */
    protected ImNode balance(List<ImNode> items)
    {
        if (items.size() > 0)
        {
            // 根据balance值由小到大排序
            Collections.sort(items);

            // 返回balance值最小的那个
            ImNode node = items.get(0);

            log.info("最佳的节点为：{}", JsonUtil.pojoToJson(node));
            return node;
        } else
        {
            return null;
        }
    }


    /*
     * 从zookeeper中拿到所有IM节点
     * 此方法通过连接Zookeeper客户端，获取指定路径下所有子节点的信息，
     * 并将这些信息转换为IM节点对象列表返回
     *
     * @return 返回IM节点对象列表，如果获取失败或解析失败则返回null
     */
    public List<ImNode> getWorkers()
    {

        List<ImNode> workers = new ArrayList<ImNode>();/*创建一个空的IM节点列表，用于存储从Zookeeper获取的节点信息*/

        List<String> children = null;
        try  /*尝试获取Zookeeper指定路径下的所有子节点名称*/
        {
            children = client.getChildren().forPath(managerPath);
        } catch (Exception e)
        {
            e.printStackTrace();/*// 如果获取子节点过程中发生异常，则打印异常信息并返回null*/
            return null;
        }
        // 遍历获取到的子节点名称列表
        for (String child : children)
        {
            log.info("child:", child);
            byte[] payload = null;
            try   /* 尝试获取子节点的数据*/
            {
                payload = client.getData().forPath(managerPath + "/" + child);

            } catch (Exception e)
            {
                e.printStackTrace();
            }
            if (null == payload) /*如果节点数据为空，则跳过当前节点*/
            {
                continue;
            }
            // 将节点数据转换为ImNode对象，并设置ID
            ImNode node = JsonUtil.jsonBytes2Object(payload, ImNode.class);
            node.setId(getIdByPath(child));
            // 将IM节点添加到列表中
            workers.add(node);
        }
        // 返回所有IM节点列表
        return workers;

    }
    /*
     * 取得IM 节点编号
     *
     * @param path 路径
     * @return 编号
     */
    public long getIdByPath(String path) {
        String sid = null; //初始化sid为null，用于后序存储解析出的编号字符串
        if (null == path) {  //检查传入的的路径是否null，若null则抛异常
            throw new RuntimeException("节点路径有误");
        }
        // 找到最后一个路径分隔符的索引，用于提取路径中的编号部分
        int index = path.lastIndexOf(ServerConstants.PATH_PREFIX_NO_STRIP);
        if (index >= 0) {
            // 移动索引到路径分隔符之后，准备提取编号字符串
            index += ServerConstants.PATH_PREFIX_NO_STRIP.length();
            // 检查索引是否在路径字符串长度范围内，防止数组越界异常
            sid = index <= path.length() ? path.substring(index) : null;
        }

        // 如果sid为null，说明未能成功提取编号字符串，抛出异常
        if (null == sid) {
            throw new RuntimeException("节点ID获取失败");
        }
// 将提取的编号字符串转换为长整型并返回
        return Long.parseLong(sid);

    }

    /*
     * 从zookeeper中删除所有IM节点
     * 从方法用于清理zookeeper中不再需要的IM工作节点
     * 它会删除指定路径下的所有子节点，以支持重新配置或清理环境
     */
    public void removeWorkers()
    {


        try
        {
                   // 尝试删除managerPath路径下的所有子节点，如果需要的话
                    // 这里的client是指向zookeeper的连接客户端，通过它执行删除操作
            client.delete().deletingChildrenIfNeeded().forPath(managerPath);
        } catch (Exception e)
        {
            // 异常处理：打印异常的堆栈跟踪信息，以便于问题排查
            e.printStackTrace();
        }


    }

}